/*
 * Copyright (c) 2023-2025 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */
/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "usb_osal.h"

#include <stdio.h>
#include <math.h>
#include "board.h"
#include "board_ex.h"
#include "hpm_gpio_drv.h"
#include "dhserver.h"
#include "dnserver.h"
#include "netif/etharp.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "lwip/icmp.h"
#include "lwip/udp.h"
#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/api.h"
#include "lwip/inet.h"
#include "lwip/dns.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "httpd.h"
#include "usbd_core.h"
#include "usbd_rndis.h"
#include "cdc_rndis_device.h"

#include "ecat_config.h"
#include "ecat_def.h"
#include "ecatappl.h"
#include "ecatslv.h"
#include "applInterface.h"
#include "digital_io.h"
#include "hpm_ecat_hw.h"

#include "net.h"

/* Macro Definition */
#define LWIP_SYS_TIME_MS 1
#define NUM_DHCP_ENTRY   3
#define PADDR(ptr)       ((ip_addr_t *)ptr)

/* Macro Definition - Task */
#define usbOSAL_PRIORITY         (0U)
#define ecat_PRIORITY            (4U)
#define mongoose_PRIORITY        (3U)
#define ds18b20_PRIORITY         (1U)

/* Static Variable Definition*/
static uint8_t hwaddr[6]  = { 0x20, 0x89, 0x84, 0x6A, 0x96, 00 };
static uint8_t ipaddr[4]  = { 192, 168, 7, 1 };
static uint8_t netmask[4] = { 255, 255, 255, 0 };
static uint8_t gateway[4] = { 0, 0, 0, 0 };

static dhcp_entry_t entries[NUM_DHCP_ENTRY] = {
    /* mac    ip address        subnet mask        lease time */
    { { 0 }, { 192, 168, 7, 2 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
    { { 0 }, { 192, 168, 7, 3 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
    { { 0 }, { 192, 168, 7, 4 }, { 255, 255, 255, 0 }, 24 * 60 * 60 }
};

static dhcp_config_t dhcp_config = {
    { 192, 168, 7, 1 }, /* server address */
    67,                 /* port */
    { 192, 168, 7, 1 }, /* dns server */
    "hpm",              /* dns suffix */
    NUM_DHCP_ENTRY,     /* num entry */
    entries             /* entries */
};

static struct netif netif_data;

/* Static Function Declaration */
static void ecat_init(void);
static void  user_init_lwip(void);
static err_t netif_init_cb(struct netif *netif);
static err_t linkoutput_fn(struct netif *netif, struct pbuf *p);
static void  lwip_service_traffic(void);
static bool  dns_query_proc(const char *name, ip_addr_t *addr);
static void usb_osal_task(void *pvParameters);
static void mongoose_task(void *pvParameters);
static void ecat_task(void *pvParameters);
static void ds18b20_task(void *pvParameters);

int main(void)
{
    board_init();
    init_esc_in_out_pin();

    /* Initialize usb */
    board_init_usb((USB_Type *)CONFIG_HPM_USBD_BASE);

    /* Set irq priority */
    intc_set_irq_priority(CONFIG_HPM_USBD_IRQn, 1);

    /* BUSID must be 0 */
    cdc_rndis_init(0, CONFIG_HPM_USBD_BASE);    

    /* Start lwip */
    user_init_lwip();
    while (!netif_is_up(&netif_data)) {
        ;
    }
    while (dhserv_init(&dhcp_config) != ERR_OK) {
        ;
    }
    while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc) != ERR_OK) {
        ;
    }
    httpd_init();

    /* Start task */
    if (usb_osal_thread_create("usb_osal_task", 8192U, usbOSAL_PRIORITY, usb_osal_task, NULL) == NULL) {
        printf("usb_osal_task creation failed!.\n");
        for (;;) {
            ;
        }
    }
    TaskHandle_t htask = NULL;
    xTaskCreate(mongoose_task, "mongoose_task", 8192U / sizeof(StackType_t), NULL, mongoose_PRIORITY, &htask);
    if (htask == NULL) {
        printf("Create thread mongoose_task failed\r\n");
        while (1) {
        }
    }
    htask = NULL;
    xTaskCreate(ecat_task, "ecat_task", 4096U / sizeof(StackType_t), NULL, ecat_PRIORITY, &htask);
    if (htask == NULL) {
        printf("Create thread ecat_task failed\r\n");
        while (1) {
        }
    }
    htask = NULL;
    xTaskCreate(ds18b20_task, "ds18b20_task", 1024U / sizeof(StackType_t), NULL, ds18b20_PRIORITY, &htask);
    if (htask == NULL) {
        printf("Create thread ds18b20_task failed\r\n");
        while (1) {
        }
    }
    vTaskStartScheduler();

    for (;;) {
        ;
    }

    return 0;
}

static void ecat_init(void)
{
    hpm_stat_t stat;

    board_init_ethercat(HPM_ESC); /* init ESC function pins */
    //board_init_switch_led();      /* init switch and led for ECAT display */

    /* hardware init */
    stat = ecat_hardware_init(HPM_ESC);
    if (stat != status_success) {
        printf("Init ESC peripheral and related devices(EEPROM/PHY) failed!\n");
        return;
    }

    /* SSC Initilize the stack */
    MainInit(); 

#if defined(ESC_EEPROM_EMULATION) && ESC_EEPROM_EMULATION
    pAPPL_EEPROM_Read  = ecat_eeprom_emulation_read;
    pAPPL_EEPROM_Write = ecat_eeprom_emulation_write;
    pAPPL_EEPROM_Reload = ecat_eeprom_emulation_reload;
    pAPPL_EEPROM_Store  = ecat_eeprom_emulation_store;
#endif

    /* Create basic mapping */
    APPL_GenerateMapping(&nPdInputSize, &nPdOutputSize);

    /* Set stack run flag */
    bRunApplication = TRUE;
}


static void user_init_lwip(void)
{
    struct netif *netif = &netif_data;

    /* Initialize the LwIP stack */
    tcpip_init(NULL, NULL);
    netif->hwaddr_len = 6;
    memcpy(netif->hwaddr, hwaddr, 6);

    netif = netif_add(netif, PADDR(ipaddr), PADDR(netmask), PADDR(gateway), NULL, netif_init_cb, netif_input);
    netif_set_default(netif);
}

static err_t netif_init_cb(struct netif *netif)
{
    LWIP_ASSERT("netif != NULL", (netif != NULL));
    netif->mtu        = 1500;
    netif->flags      = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
    netif->state      = NULL;
    netif->name[0]    = 'E';
    netif->name[1]    = 'X';
    netif->linkoutput = linkoutput_fn;
    netif->output     = etharp_output;
    return ERR_OK;
}

static err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
{
    extern usb_osal_sem_t sema_rndis_send_done;
    (void)netif;
    int ret;

    ret = usbd_rndis_eth_tx(p);

    if (0 != ret) {
        ret = ERR_BUF;
    } else {
        if (usb_osal_sem_take(sema_rndis_send_done, portMAX_DELAY) != 0) {
            ret = ERR_BUF;
        }
    }
    return ret;
}

static void lwip_service_traffic(void)
{
    err_t        err;
    struct pbuf *p;

    p = usbd_rndis_eth_rx();

    if (p != NULL) {
        /* entry point to the LwIP stack */
        err = netif_data.input(p, &netif_data);

        if (err != ERR_OK) {
            LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
            pbuf_free(p);
        }
    }
}

static bool dns_query_proc(const char *name, ip_addr_t *addr)
{
    if (strcmp(name, "rndis.hpm") == 0 || strcmp(name, "www.rndis.hpm") == 0) {
        addr->addr = *(uint32_t *)ipaddr;
        return true;
    }
    return false;
}

static void usb_osal_task(void *pvParameters)
{
    extern usb_osal_sem_t sema_rndis_data;
    (void)pvParameters;
    printf("[cherry usb rndis device with freertos]: task started.\n");
    while (1) {
        if (usb_osal_sem_take(sema_rndis_data, portMAX_DELAY) != 0) {
            continue;
        }
        //lwip_service_traffic();
        tcpip_callback((tcpip_callback_fn) lwip_service_traffic, NULL);
    }
}

static void mongoose_task(void *pvParameters)
{
    (void)pvParameters;

    printf("[mongoose server]: task started.\n");

    struct mg_mgr mgr;
    mg_log_set(MG_LL_DEBUG); 
    mg_mgr_init(&mgr);
    web_init(&mgr);
    while (1) {
        mg_mgr_poll(&mgr, 5);
        vTaskDelay(5);
    }
    mg_mgr_free(&mgr);
}

static void ecat_task(void *pvParameters)
{
    (void)pvParameters;

    printf("[ecat task]: task started.\n");
      
    ecat_init();

    while (bRunApplication) {
        MainLoop();
        vTaskDelay(1);
    }

    while (1) {
        vTaskDelay(1000);
    }
}

static void ds18b20_task(void *pvParameters)
{
    (void)pvParameters;

    printf("[ds18b20 task]: task started.\n");
    
    board_init2();

    while (1) 
    {
        board_update_ds18b20();
        vTaskDelay(1000);
    }
}